package com.whosly.mysql.protocol.packet.impl;

import com.google.common.collect.Maps;
import com.whosly.mysql.protocol.jdbc.MySQLMessage;
import com.whosly.mysql.protocol.packet.MysqlPackConstants;
import com.whosly.mysql.protocol.packet.jdbc.AbstractMySQLPacket;
import com.whosly.mysql.protocol.packet.util.NettyBufferUtil;
import io.netty.buffer.ByteBuf;
import lombok.Getter;
import lombok.Setter;

import java.util.Map;

/**
 * <table>
 *   <thead>
 *     <tr>
 *       <th>类型值</th>
 *       <th>命令</th>
 *       <th>功能</th>
 *       <th>关联函数</th>
 *     </tr>
 *   </thead>
 *   <tbody>
 *     <tr>
 *       <td>0x00</td>
 *       <td>COM_SLEEP</td>
 *       <td>（内部线程状态）</td>
 *       <td>（无）</td>
 *     </tr>
 *     <tr>
 *       <td>0x01</td>
 *       <td>COM_QUIT</td>
 *       <td>关闭连接</td>
 *       <td>mysql_close</td>
 *     </tr>
 *     <tr>
 *       <td>0x02</td>
 *       <td>COM_INIT_DB</td>
 *       <td>切换数据库</td>
 *       <td>mysql_select_db</td>
 *     </tr>
 *     <tr>
 *       <td>0x03</td>
 *       <td>COM_QUERY</td>
 *       <td>SQL查询请求</td>
 *       <td>mysql_real_query</td>
 *     </tr>
 *     <tr>
 *       <td>0x04</td>
 *       <td>COM_FIELD_LIST</td>
 *       <td>获取数据表字段信息</td>
 *       <td>mysql_list_fields</td>
 *     </tr>
 *     <tr>
 *       <td>0x05</td>
 *       <td>COM_CREATE_DB</td>
 *       <td>创建数据库</td>
 *       <td>mysql_create_db</td>
 *     </tr>
 *     <tr>
 *       <td>0x06</td>
 *       <td>COM_DROP_DB</td>
 *       <td>删除数据库</td>
 *       <td>mysql_drop_db</td>
 *     </tr>
 *     <tr>
 *       <td>0x07</td>
 *       <td>COM_REFRESH</td>
 *       <td>清除缓存</td>
 *       <td>mysql_refresh</td>
 *     </tr>
 *     <tr>
 *       <td>0x08</td>
 *       <td>COM_SHUTDOWN</td>
 *       <td>停止服务器</td>
 *       <td>mysql_shutdown</td>
 *     </tr>
 *     <tr>
 *       <td>0x09</td>
 *       <td>COM_STATISTICS</td>
 *       <td>获取服务器统计信息</td>
 *       <td>mysql_stat</td>
 *     </tr>
 *     <tr>
 *       <td>0x0A</td>
 *       <td>COM_PROCESS_INFO</td>
 *       <td>获取当前连接的列表</td>
 *       <td>mysql_list_processes</td>
 *     </tr>
 *     <tr>
 *       <td>0x0B</td>
 *       <td>COM_CONNECT</td>
 *       <td>（内部线程状态）</td>
 *       <td>（无）</td>
 *     </tr>
 *     <tr>
 *       <td>0x0C</td>
 *       <td>COM_PROCESS_KILL</td>
 *       <td>中断某个连接</td>
 *       <td>mysql_kill</td>
 *     </tr>
 *     <tr>
 *       <td>0x0D</td>
 *       <td>COM_DEBUG</td>
 *       <td>保存服务器调试信息</td>
 *       <td>mysql_dump_debug_info</td>
 *     </tr>
 *     <tr>
 *       <td>0x0E</td>
 *       <td>COM_PING</td>
 *       <td>测试连通性</td>
 *       <td>mysql_ping</td>
 *     </tr>
 *     <tr>
 *       <td>0x0F</td>
 *       <td>COM_TIME</td>
 *       <td>（内部线程状态）</td>
 *       <td>（无）</td>
 *     </tr>
 *     <tr>
 *       <td>0x10</td>
 *       <td>COM_DELAYED_INSERT</td>
 *       <td>（内部线程状态）</td>
 *       <td>（无）</td>
 *     </tr>
 *     <tr>
 *       <td>0x11</td>
 *       <td>COM_CHANGE_USER</td>
 *       <td>重新登陆（不断连接）</td>
 *       <td>mysql_change_user</td>
 *     </tr>
 *     <tr>
 *       <td>0x12</td>
 *       <td>COM_BINLOG_DUMP</td>
 *       <td>获取二进制日志信息</td>
 *       <td>（无）</td>
 *     </tr>
 *     <tr>
 *       <td>0x13</td>
 *       <td>COM_TABLE_DUMP</td>
 *       <td>获取数据表结构信息</td>
 *       <td>（无）</td>
 *     </tr>
 *     <tr>
 *       <td>0x14</td>
 *       <td>COM_CONNECT_OUT</td>
 *       <td>（内部线程状态）</td>
 *       <td>（无）</td>
 *     </tr>
 *     <tr>
 *       <td>0x15</td>
 *       <td>COM_REGISTER_SLAVE</td>
 *       <td>从服务器向主服务器进行注册</td>
 *       <td>（无）</td>
 *     </tr>
 *     <tr>
 *       <td>0x16</td>
 *       <td>COM_STMT_PREPARE</td>
 *       <td>预处理SQL语句</td>
 *       <td>mysql_stmt_prepare</td>
 *     </tr>
 *     <tr>
 *       <td>0x17</td>
 *       <td>COM_STMT_EXECUTE</td>
 *       <td>执行预处理语句</td>
 *       <td>mysql_stmt_execute</td>
 *     </tr>
 *     <tr>
 *       <td>0x18</td>
 *       <td>COM_STMT_SEND_LONG_DATA</td>
 *       <td>发送BLOB类型的数据</td>
 *       <td>mysql_stmt_send_long_data</td>
 *     </tr>
 *     <tr>
 *       <td>0x19</td>
 *       <td>COM_STMT_CLOSE</td>
 *       <td>销毁预处理语句</td>
 *       <td>mysql_stmt_close</td>
 *     </tr>
 *     <tr>
 *       <td>0x1A</td>
 *       <td>COM_STMT_RESET</td>
 *       <td>清除预处理语句参数缓存</td>
 *       <td>mysql_stmt_reset</td>
 *     </tr>
 *     <tr>
 *       <td>0x1B</td>
 *       <td>COM_SET_OPTION</td>
 *       <td>设置语句选项</td>
 *       <td>mysql_set_server_option</td>
 *     </tr>
 *     <tr>
 *       <td>0x1C</td>
 *       <td>COM_STMT_FETCH</td>
 *       <td>获取预处理语句的执行结果</td>
 *       <td>mysql_stmt_fetch</td>
 *     </tr>
 *   </tbody>
 * </table>
 *
 * bytes[4] == type
 *
 * @author 风杨
 * @see <a href="yueny09@163.com">mailTo: yueny09@163.com</a>
 *
 * @see `MySQL` include/mysql_com.h
 */
public class CommandPacket extends AbstractMySQLPacket
{

    /**
     * The most common value is 03 COM_QUERY, because INSERT UPDATE DELETE SELECT etc. have this code.
     *
     * The possible values at time of writing (taken
     * from /include/mysql_com.h for enum_server_command) see CommandPacket
     */
    @Getter
    @Setter
    private byte cmd;

    @Override
    public ByteBuf writeBuf(ByteBuf buffer)
    {
        NettyBufferUtil.writeUB3(buffer, buffer.capacity());
        buffer.writeByte(getPackId());

        buffer.writeByte(this.cmd);

        return buffer;
    }

    @Override
    public void read(byte[] bodyBytes)
    {
        readBody(bodyBytes);
    }

    protected void readBody(byte[] bodyBytes) {
        MySQLMessage mm = new MySQLMessage(bodyBytes);

        // eg: is  3
        this.cmd = mm.read();
    }

    /**
     * @param bytes mysql协议数据
     * @return Command 指令
     */
    public static String getPacketName(final byte[] bytes) {
        if (bytes.length >= 5) {
            return COMMAND_NAMES.get(bytes[MysqlPackConstants.COMMAND_POSITION]);
        } else {
            return "UNKONW";
        }
    }

    @Override
    public int calcPacketSize() {
        int packLength = 1;
        return packLength;
    }

    /**
     * 取得数据包信息
     */
    @Override
    public String getPacketInfo() {
        return "MySQL Command Packet";
    }


    /*
     *               Name                Associated client function
     *                -      ----                --------------------------
     *
     *                0x00   COM_SLEEP           (none, this is an internal thread state)
     *                0x01   COM_QUIT            mysql_close
     *                0x02   COM_INIT_DB         mysql_select_db
     *                0x03   COM_QUERY           mysql_real_query
     *                0x04   COM_FIELD_LIST      mysql_list_fields
     *                0x05   COM_CREATE_DB       mysql_create_db (deprecated)
     *                0x06   COM_DROP_DB         mysql_drop_db (deprecated)
     *                0x07   COM_REFRESH         mysql_refresh
     *                0x08   COM_SHUTDOWN        mysql_shutdown
     *                0x09   COM_STATISTICS      mysql_stat
     *                0x0a   COM_PROCESS_INFO    mysql_list_processes
     *                0x0b   COM_CONNECT         (none, this is an internal thread state)
     *                0x0c   COM_PROCESS_KILL    mysql_kill
     *                0x0d   COM_DEBUG           mysql_dump_debug_info
     *                0x0e   COM_PING            mysql_ping
     *                0x0f   COM_TIME            (none, this is an internal thread state)
     *                0x10   COM_DELAYED_INSERT  (none, this is an internal thread state)
     *                0x11   COM_CHANGE_USER     mysql_change_user
     *                0x12   COM_BINLOG_DUMP     sent by the slave IO thread to request a binlog
     *                0x13   COM_TABLE_DUMP      LOAD TABLE ... FROM MASTER (deprecated)
     *                0x14   COM_CONNECT_OUT     (none, this is an internal thread state)
     *                0x15   COM_REGISTER_SLAVE  sent by the slave to register with the master (optional)
     *                0x16   COM_STMT_PREPARE    mysql_stmt_prepare
     *                0x17   COM_STMT_EXECUTE    mysql_stmt_execute
     *                0x18   COM_STMT_SEND_LONG_DATA mysql_stmt_send_long_data
     *                0x19   COM_STMT_CLOSE      mysql_stmt_close
     *                0x1a   COM_STMT_RESET      mysql_stmt_reset
     *                0x1b   COM_SET_OPTION      mysql_set_server_option
     *                0x1c   COM_STMT_FETCH      mysql_stmt_fetch
     */
    public static final byte COMMAND_SLEEP = 0x00; // (none, this is an internal thread state)
    public static final byte COMMAND_QUIT = 0x01; // mysql_close, byte type = 1
    public static final byte COMMAND_INIT_DB = 0x02; // mysql_select_db

    public static final byte COMMAND_QUERY = 0x03; // mysql_real_query
    public static final byte COMMAND_FIELD_LIST = 0x04; // mysql_list_fields
    public static final byte COMMAND_CREATE_DB = 0x05; // mysql_create_db (deprecated)

    public static final byte COMMAND_DROP_DB = 0x06; // mysql_drop_db (deprecated)
    public static final byte COMMAND_REFRESH = 0x07; // mysql_refresh
    public static final byte COMMAND_SHUTDOWN = 0x08; // mysql_shutdown

    public static final byte COMMAND_STATISTICS = 0x09; // mysql_stat
    public static final byte COMMAND_PROCESS_INFO = 0x0a; // mysql_list_processes, byte = 10
    public static final byte COMMAND_CONNECT = 0x0b; // (none, this is an internal thread state)

    public static final byte COMMAND_PROCESS_KILL = 0x0c; // mysql_kill
    public static final byte COMMAND_DEBUG = 0x0d; // mysql_dump_debug_info
    public static final byte COMMAND_PING = 0x0e; // mysql_ping, byte type = 14

    public static final byte COMMAND_TIME = 0x0f; // (none, this is an internal thread state)
    public static final byte COMMAND_DELAYED_INSERT = 0x10; // (none, this is an internal thread state), byte = 16
    public static final byte COMMAND_CHANGE_USER = 0x11; // mysql_change_user, 17

    public static final byte COMMAND_BINLOG_DUMP = 0x12; // (used by slave server / mysqlbinlog), byte = 18
    public static final byte COMMAND_TABLE_DUMP = 0x13; // (used by slave server to get master table)
    public static final byte COMMAND_CONNECT_OUT = 0x14; // (used by slave to log connection to master)

    public static final byte COMMAND_REGISTER_SLAVE = 0x15; // (used by slave to register to master)
    public static final byte COMMAND_STMT_PREPARE = 0x16; // mysql_stmt_prepare
    public static final byte COMMAND_STMT_EXECUTE = 0x17; // mysql_stmt_execute

    public static final byte COMMAND_STMT_SEND_LONG_DATA = 0x18; // mysql_stmt_send_long_data, byte type = 24
    public static final byte COMMAND_STMT_CLOSE = 0x19; // mysql_stmt_close, byte = 25
    public static final byte COMMAND_STMT_RESET = 0x1a; // mysql_stmt_reset

    public static final byte COMMAND_SET_OPTION = 0x1b; // mysql_set_server_option, byte = 27
    public static final byte COMMAND_STMT_FETCH = 0x1c; // mysql_stmt_fetch, byte = 28
    public static final byte COMMAND_EOF = (byte) 0xfe; // eof, byte = 254

    /*
     * byte = 41
     *
     * MY_COM_RDS_AUTH_AND_SWITCH:
     * 29, Username, 0, clientip, 0, size(scramble), scramble, 0, size(authdata), authdata
     */
    public static final byte MY_COM_RDS_AUTH_AND_SWITCH = 0x29;

    /*
     * byte = 40
     *
     * MY_COM_RDS_AUTH:
     * 28, Username, 0, clientip, 0, size(Scramble), scramble, 0, size(authdata), authdata
     */
    public static final byte MY_COM_RDS_AUTH = 0x28;

    public static final Map<Byte, String> COMMAND_NAMES = Maps.newHashMap();

    {
        COMMAND_NAMES.put(COMMAND_SLEEP, "sleep");
        COMMAND_NAMES.put(COMMAND_QUIT, "mysql_close");
        COMMAND_NAMES.put(COMMAND_INIT_DB, "mysql_select_db");
        COMMAND_NAMES.put(COMMAND_QUERY, "mysql_real_query");
        COMMAND_NAMES.put(COMMAND_FIELD_LIST, "mysql_list_fields");
        COMMAND_NAMES.put(COMMAND_CREATE_DB, "mysql_create_db (deprecated)");
        COMMAND_NAMES.put(COMMAND_DROP_DB, "mysql_drop_db (deprecated)");
        COMMAND_NAMES.put(COMMAND_REFRESH, "mysql_refresh");
        COMMAND_NAMES.put(COMMAND_SHUTDOWN, "mysql_shutdown");
        COMMAND_NAMES.put(COMMAND_STATISTICS, "mysql_stat");
        COMMAND_NAMES.put(COMMAND_PROCESS_INFO, "mysql_list_processes");
        COMMAND_NAMES.put(COMMAND_CONNECT, "connect");
        COMMAND_NAMES.put(COMMAND_PROCESS_KILL, "mysql_kill");
        COMMAND_NAMES.put(COMMAND_DEBUG, "mysql_dump_debug_info");
        COMMAND_NAMES.put(COMMAND_PING, "mysql_ping");
        COMMAND_NAMES.put(COMMAND_TIME, "mysql_time");
        COMMAND_NAMES.put(COMMAND_DELAYED_INSERT, "mysql_delayed_insert");
        COMMAND_NAMES.put(COMMAND_CHANGE_USER, "mysql_change_user");
        COMMAND_NAMES.put(COMMAND_BINLOG_DUMP, "binlog_dump(used by slave server / mysqlbinlog)");
        COMMAND_NAMES.put(COMMAND_TABLE_DUMP, "table_dump(used by slave server to get master table)");
        COMMAND_NAMES.put(COMMAND_CONNECT_OUT, "connect_out(used by slave to log connection to master)");
        COMMAND_NAMES.put(COMMAND_REGISTER_SLAVE, "register_slave(used by slave to register to master)");
        COMMAND_NAMES.put(COMMAND_STMT_PREPARE, "mysql_stmt_prepare");
        COMMAND_NAMES.put(COMMAND_STMT_EXECUTE, "mysql_stmt_execute");
        COMMAND_NAMES.put(COMMAND_STMT_SEND_LONG_DATA, "mysql_stmt_send_long_data");
        COMMAND_NAMES.put(COMMAND_STMT_CLOSE, "mysql_stmt_close");
        COMMAND_NAMES.put(COMMAND_STMT_RESET, "mysql_stmt_reset");
        COMMAND_NAMES.put(COMMAND_SET_OPTION, "mysql_set_server_option");
        COMMAND_NAMES.put(COMMAND_STMT_FETCH, "mysql_stmt_fetch");
        COMMAND_NAMES.put(COMMAND_EOF, "mysql_eof");
    }

}
